/******************************************************************************* * Copyright (c) 2006-2010 eBay Inc. All Rights Reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 *******************************************************************************/ //B''H package org.ebayopensource.turmeric.runtime.sif.service; import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.Executor; import java.util.logging.Logger; import javax.xml.namespace.QName; import javax.xml.ws.Response; import javax.xml.ws.WebServiceException; import org.ebayopensource.turmeric.runtime.binding.objectnode.ObjectNode; import org.ebayopensource.turmeric.runtime.binding.objectnode.impl.JavaObjectNodeImpl; import org.ebayopensource.turmeric.runtime.common.cachepolicy.CacheContext; import org.ebayopensource.turmeric.runtime.common.cachepolicy.CacheProvider; import org.ebayopensource.turmeric.runtime.common.exceptions.ServiceException; import org.ebayopensource.turmeric.runtime.common.exceptions.ServiceInvocationException; import org.ebayopensource.turmeric.runtime.common.impl.internal.utils.IAsyncResponsePoller; import org.ebayopensource.turmeric.runtime.common.impl.utils.LogManager; import org.ebayopensource.turmeric.runtime.common.impl.utils.ReflectionUtils; import org.ebayopensource.turmeric.runtime.common.types.ByteBufferWrapper; import org.ebayopensource.turmeric.runtime.common.types.Cookie; import org.ebayopensource.turmeric.runtime.common.types.G11nOptions; import org.ebayopensource.turmeric.runtime.common.types.SOAConstants; import org.ebayopensource.turmeric.runtime.common.types.SOAHeaders; import org.ebayopensource.turmeric.runtime.common.utils.ThreadPoolConfig; import org.ebayopensource.turmeric.runtime.common.utils.ThreadPoolFactory; import org.ebayopensource.turmeric.runtime.sif.impl.internal.service.ClientServiceDesc; import org.ebayopensource.turmeric.runtime.sif.impl.internal.service.RawDataServiceDispatch; import org.ebayopensource.turmeric.runtime.sif.impl.internal.service.RawDispatchData; import org.ebayopensource.turmeric.runtime.sif.impl.internal.service.ServicePoller; import org.ebayopensource.turmeric.runtime.sif.impl.internal.service.TypedDataServiceDispatch; /** * This is the interface through which clients can get access to a specific * service. Clients are expected to first create an instance of a service using * the static create method (ServiceFactory.create()). Then either the * getProxy() method can be used to get a proxy object for the interface using a * specific binding or simply an invoke() method can be called on Service, to * dynamically invoke a service (referred to as dynamic invocation interface, * DII). * <P> * Clients can set various invocation options prior to invoking the service, by * calling getInvokerOptions() and setting values on that structure. Most of * these options can also be specified in configuration. For any values * specified in both configuration and ServiceInvokerOptions, the * ServiceInvokerOptions values take precedence. * <P> * If clients want to send or retrieve certain header elements, they can do so, * using the request/response contexts available (getRequestContext(), * getResponseContext()). * <P> * A Service instance is not thread safe. If a client creates multiple threads * to invoke a service simultaneously, then each thread is expected to create * its own Service instance. This is because the request/response context is * specific to an invocation. * * @author ichernyshev, smalladi, cpenkar */ public final class Service { private static final Class[] PROXY_CONSTRUCTOR_PARAM_TYPES = new Class[] { Service.class }; private static final String THREAD_POOL_NAME_SEPARATOR = "-"; private static final String THREAD_POOL_NAME_PREFIX = "ServiceAsyncProcessing"; private static final String THREAD_POOL_NAME_Suffix = "default"; private static final Executor DEFAULT_SERVICE_EXECUTOR = createDefaultExecutor(); private final ClientServiceDesc m_serviceDesc; private final URL m_wsdlLocation; private final ServiceInvokerOptions m_invokerOptions; private final String m_serviceVersion; private List<URL> m_serviceLocations; private Map<String, String> m_sessionTransportHeaders = new HashMap<String, String>(); private Map<String, Cookie> m_cookies = new HashMap<String, Cookie>(); private Collection<ObjectNode> m_sessionMessageHeaders = new ArrayList<ObjectNode>(); private Object m_proxy; private G11nOptions m_g11nOptions; private String m_urlPathInfo; private RequestContext m_requestContext = null; private ResponseContext m_responseContext = null; private Executor m_executor; private int m_numTries = 0; private IAsyncResponsePoller m_poller = new ServicePoller(); /** * Internal constructor - client application code must use * ServiceFactory.create() to construct a service object. * * @param serviceDesc * @param serviceLocation * @param serviceVersion * @param wsdlLocation * @throws ServiceException */ Service(ClientServiceDesc serviceDesc, List<URL> serviceLocations, String serviceVersion, URL wsdlLocation) throws ServiceException { if (serviceDesc == null) { throw new NullPointerException(); } m_serviceDesc = serviceDesc; if (serviceLocations != null) { m_serviceLocations = serviceLocations; } else { m_serviceLocations = serviceDesc.getServiceLocations(); } m_serviceVersion = serviceVersion; // null OK m_wsdlLocation = wsdlLocation; // null OK m_invokerOptions = new ServiceInvokerOptions(); m_executor = getDefaultExecutor(); } /** * Internal constructor - client application code must use * ServiceFactory.create() to construct a service object. * * @param serviceDesc The client service desc * @param serviceLocation The service location * @param serviceVersion The service version * @param wsdlLocation The wsdl location * @throws ServiceException Throws when there is error */ Service(ClientServiceDesc serviceDesc, URL serviceLocation, String serviceVersion, URL wsdlLocation) throws ServiceException { if (serviceDesc == null) { throw new NullPointerException(); } m_serviceDesc = serviceDesc; URL loc = null; if (serviceLocation != null) { loc = serviceLocation; } else { loc = serviceDesc.getDefServiceLocationURL(); } m_serviceLocations = new ArrayList<URL>(); m_serviceLocations.add(loc); m_serviceVersion = serviceVersion; // null OK m_wsdlLocation = wsdlLocation; // null OK m_invokerOptions = new ServiceInvokerOptions(); m_executor = getDefaultExecutor(); } /** * Retrieve the logger for this class. * @return The logger */ static Logger getLogger() { return LogManager.getInstance(Service.class); } /** * Retrieve the default thread pool executor. * @return The thread pool executor */ static Executor getDefaultExecutor() { return DEFAULT_SERVICE_EXECUTOR; } /** * Returns the administrative name of the service within this client * instance. On the client side, the administrative name is the local part * of the service qualified name configured in ClientConfig.xml. * * @return the administrative name */ public String getAdminName() { return m_serviceDesc.getAdminName(); } /** * Returns the fully qualified name of the service to be accessed by this * client instance; clients and services mutually associate this value in * order to uniquely identify the service to be invoked. * * @return the qualified name */ public QName getServiceQName() { return m_serviceDesc.getServiceQName(); } /** * Returns a proxy offering type-specific operations and conversions for the * given service. The proxy will be created from a cleass specified in the * service configuration. Proxies are usually generated by the code * generation tool. * * @param <T> type of the returning proxy object * @return the proxy object * @throws ServiceException throws when error happens */ public <T> T getProxy() throws ServiceException { if (m_proxy == null) { m_proxy = createProxy(); } @SuppressWarnings("unchecked") T result = (T) m_proxy; return result; } /** * Private method to construct proxy. * * @return the proxy * @throws ServiceException */ private Object createProxy() throws ServiceException { Class proxyClass = m_serviceDesc.getProxyClass(); @SuppressWarnings("unchecked") Object result = ReflectionUtils.createInstance(proxyClass, PROXY_CONSTRUCTOR_PARAM_TYPES, new Object[] { this }); return result; } /** * Returns the Service's invoker options structure, which is never null. The * client application code can proceed to set values into this structure, * and these will be used for the next service invocation. The same * structure remains allocated through the lifetime of this particular * Service object. * * @return ServiceInvokerOptions object */ public ServiceInvokerOptions getInvokerOptions() { return m_invokerOptions; } /** * Returns the currently set globalization options. This is an immutable * structure; to change the globalization options, allocate a new object and * use <code>setG11nOptions()</code>. * * @return the globalization options */ public G11nOptions getG11nOptions() { return m_g11nOptions; } /** * Set the globalization options. These will be used for all service * invocations until another call to setG11nOptions is made. If the value is * null, options will be taken from the client configuration for this * service. If configuration is also not available, a minimal globalization * options structure will be used, with no global ID/locale information, and * the JVM default character set (generally UTF-16). * * @param value * the globalization options to be set */ public void setG11nOptions(G11nOptions value) { m_g11nOptions = value; } /** * Returns the URL path info which is appended after the base URL. * * @return the Url path Info */ public String getUrlPathInfo() { return m_urlPathInfo; } /** * Set the Url Path info. * * @param urlPathInfo * the Url Path to be set */ public void setUrlPathInfo(String urlPathInfo) { m_urlPathInfo = urlPathInfo; } /** * Returns the request context to be used for the next service invocation. * The client application code can proceed to set values into this * structure, and these will be used for the next service invocation only. * The Service's current request context reference is discarded after * invoke() finishes, and the caller should access a new RequestContext for * the following invocation. * * @return the request context */ public RequestContext getRequestContext() { if (m_requestContext == null) { m_requestContext = new RequestContext(); } return m_requestContext; } /** * Returns the response context with results from the previous call to * invoke(). The returned context is never null. The response context * contains transport headers received from the server. * * @return the response context. */ public ResponseContext getResponseContext() { if (m_responseContext == null) { m_responseContext = new ResponseContext(); } return m_responseContext; } /** * Returns the service location (endpoint address). This is the same URL * passed to the ServiceFactory.create() call. * * @return the service location. */ public URL getServiceLocation() { if(m_serviceLocations==null || m_serviceLocations.isEmpty()) return null; return m_serviceLocations.get(0); } /* * Returns the service location (endpoint address). This is the same URL * passed to the ServiceFactory.create() call. * * @return the service location. */ /** * Sets the service location (endpoint address). * * @param sl * the new service location to use for subsequence calls. * */ public void setServiceLocation(URL sl) { m_serviceLocations = new ArrayList<URL>(); m_serviceLocations.add(sl); } public void setServiceLocations(List<URL> sl) { m_serviceLocations = sl; } /** * Returns the service WSDL URL. This feature is not currently * implemented/used within the SOA framework. * * @return the WSDL URL. */ public URL getWsdlLocation() { return m_wsdlLocation; } /** * Returns the service version. This is either the value passed to * ServiceFactory.create(), or if no such value was supplied, the version * given in the client's configuration for this service. * * @return the service version */ public String getVersion() { if (m_serviceVersion == null) { return m_serviceDesc.getServiceVersion(); } return m_serviceVersion; } /** * Returns a specified transport header value that was previously set with * <code>setSessionTransportHeader()</code>, or null if none. * * @param name * the name of the header value to be retrieved * @return the header value */ public String getSessionTransportHeader(String name) { if (m_sessionTransportHeaders == null || name == null) { return null; } name = SOAHeaders.normalizeName(name, false); return m_sessionTransportHeaders.get(name); } /** * Sets a transport header value with session level duration. Header values * set with this function are retained across future invocations until * changed (another call is made with the same name and a different value) * or until the Service object is destroyed. In contrast, header values set * into the RequestContext have effect only until the next service * invocation completes. RequestContext header values should be considered * temporary overrides of the more permanent session header values here. * * @param name * the name of the header value to set * @param value * the value to be set */ public void setSessionTransportHeader(String name, String value) { if (name == null) { throw new NullPointerException(); } name = SOAHeaders.normalizeName(name, false); value = SOAHeaders.normalizeValue(name, value); m_sessionTransportHeaders.put(name, value); } /** * Returns a copy of the entire map of session transport headers. Refer to * <code>setSessionTransportHeader()</code>. * * @return the session transport map */ public Map<String, String> getSessionTransportHeaders() { return Collections.unmodifiableMap(new HashMap<String, String>( m_sessionTransportHeaders)); } /** * Add a message header object as ObjectNode with session level duration. * Header objects set with this function are retained across future * invocations until changed (another call is made with the same name and a * different value) or until the Service object is destroyed. In contrast, * header objects set into the RequestContext have effect only until the * next service invocation completes. RequestContext header objects should * be considered temporary overrides of the more permanent session header * objects here. * * @param objectNode * message header object */ public void addSessionMessageHeader(ObjectNode objectNode) { if (objectNode == null) { return; } m_sessionMessageHeaders.add(objectNode); } /** * Add a message header as an Java Object with session level duration. * Header objects set with this function are retained across future * invocations until changed (another call is made with the same name and a * different value) or until the Service object is destroyed. In contrast, * header objects set into the RequestContext have effect only until the * next service invocation completes. RequestContext header objects should * be considered temporary overrides of the more permanent session header * objects here. * * @param headerJavaObject * message header object */ public void addSessionMessageHeaderAsJavaObject(Object headerJavaObject) { if (headerJavaObject == null) { return; } m_sessionMessageHeaders.add(new JavaObjectNodeImpl(null, headerJavaObject)); } /** * Returns a copy of the entire map of session message headers. * * @return the session message header collection */ public Collection<ObjectNode> getSessionMessageHeaders() { return Collections.unmodifiableCollection(new ArrayList<ObjectNode>( m_sessionMessageHeaders)); } /** * Sets a cookie with session level duration. Cookiess set with this * function are retained across future invocations until changed (another * set is made of a cookie with the same name) or until the Service object * is destroyed. In contrast, cookies set into the RequestContext have * effect only until the next service invocation completes. RequestContext * cookies should be considered temporary overrides of the more permanent * session header cookies here. * * @param cookie * the cookie to be set */ public void setCookie(Cookie cookie) { m_cookies.put(cookie.getName(), cookie); } /** * Returns a specified session cookie value by name that was previously set * with <code>setCookie()</code>. * * @param name * the name of the cookie value to be retrieved. * @return the specified session cookie, or null if none. */ public Cookie getCookie(String name) { if (name != null && m_cookies != null) { return m_cookies.get(name.toUpperCase()); } return null; } /** * Returns all session cookie values. Refer to <code>setCookie()</code>. * * @return the session cookies */ public Cookie[] getCookies() { Cookie[] result = m_cookies.values().toArray( new Cookie[m_cookies.size()]); return result; } /** * Retrieves the cookies. * @return The map containing the cookies. Key is the cookie name and value is the cookie. */ Map<String, Cookie> getCookiesMap() { return Collections.unmodifiableMap(new HashMap<String, Cookie>( m_cookies)); } /** * This generic invocation method takes untyped input, output, and error * argument information. invoke() constitutes the "Dynamic Invocation * Interface" (DII) of the SOA Client. Any operation of a service can be * invoked through this method, simply by giving the operation name. * * Alternatively, the client application code can obtain a proxy using * <code>getProxy()</code>. * * @param opName * the name of the operation to be invoked * @param inParams * an array of input parameters, of the appropriate Java types as * configured for the service (same as in the corresponding proxy * method signature and as in the type mappings configuration * that is generated by code generation). * @param outParams * an array of output parameters, of the appropriate Java types * (as per input parameters). * * @throws ServiceInvocationException * in case there are client or server side system errors, or if * there are application errors in the returned error response. */ public void invoke(String opName, Object[] inParams, List<Object> outParams) throws ServiceInvocationException { invoke(opName, false, false, inParams, outParams, null, null); } /** * * @param block * Specifies if the poll method is blocking or non-blocking. * @param partial * Specifies if the poll method returns list or ALL/PARTIAL * responses * @return if block is true and if partial is true then method is blocked * until at least one response. It may return more than one. b. if * block is true and if partial is false then method is blocked * until all the outstanding responses are available. c. if block * is false and if partial is true then method is non-blocking and * returns immediately all available responses. this is option is * typical polling with no-timeout d. if block is false and if * partial is false then method is BLOCKED until all the * outstanding responses are available. * * Note: if partial is false, Irrespective of "block" parameter, all * outstanding responses are returned and method call is BLOCKING * @throws InterruptedException throws if the polling gets interrupted during the waiting */ public List<Response<?>> poll(boolean block, boolean partial) throws InterruptedException { return m_poller.poll(block, partial, -1); } /** * * @param block * Specifies if the poll method is blocking or non-blocking. * @param partial * Specifies if the poll method returns list or ALL/PARTIAL * responses * @param timeout * timeout in milliseconds * @return if block is true and if partial is true then method is blocked * until atleast one response. It may return more than one. b. if * block is true and if partial is false then method is blocked * until all the outstanding responses are available. c. if block * is false and if partial is true then method is non-blocking and * returns immediately all available responses. this is option is * typical polling with no-timeout d. if block is false and if * partial is false then method is BLOCKED until all the * outstanding responses are available. * * Note: if partial is false, Irrespective of "block" parameter, all * outstanding responses are returned and method call is BLOCKING * @throws InterruptedException throws if the polling gets interrupted during the waiting */ public List<Response<?>> poll(boolean block, boolean partial, long timeout) throws InterruptedException { return m_poller.poll(block, partial, timeout); } /** * This generic invocation method facilitates working with serialized data, * wrapped in a ByteBufferWrapper. Client applications that prefer to * manipulate serialized request/response representations may call this * method and pass in a pre-serialized request that will be transmitted to * the service without modification. Clients will then receive a * non-deserialized result back. * * Takes in pre-serialized input buffer wrapper, output buffer wrapper that * acts as a holder for result coming back. This invoke() constitutes the * alternate "Dynamic Invocation Interface" (DII) of the SOA Client. Any * operation of a service can be invoked through this method, simply by * giving the operation name. * * Alternatively, the client application code can obtain a proxy using * <code>getProxy()</code>. * * Note: since we don't know the size of the response buffer, we cannot * preallocate space for it and therefore are using a wrapper. * * @param headerMap * header map containing the name of the operation to be invoked * @param inWrapper * a wrapper containing a ByteBuffer with pre-serialized request * @param outWrapper * a place holder for non-deserialized result ByteBuffer, must be * initialized * * @throws ServiceInvocationException * in case there are client or server side system errors, or if * there are application errors in the returned error response. */ public void invoke(Map<String, String> headerMap, ByteBufferWrapper inWrapper, ByteBufferWrapper outWrapper) throws ServiceInvocationException { if (headerMap == null) { throw new IllegalArgumentException("Header map cannot be null"); } String opName = headerMap.get(SOAHeaders.SERVICE_OPERATION_NAME); RequestContext ctx = getRequestContext(); for (Map.Entry<String, String> e : headerMap.entrySet()) { ctx.setTransportHeader(e.getKey(), e.getValue()); } invoke(opName, true, true, null, null, inWrapper, outWrapper); } /** * For serializing the request, but skipping the response deserialization. * @param opName the name of the operation to be invoked * @param inParams * an array of input parameters, of the appropriate Java types as * configured for the service (same as in the corresponding proxy * method signature and as in the type mappings configuration * that is generated by code generation). * @param outWrapper * a place holder for non-deserialized result ByteBuffer, must be * initialized * @throws ServiceInvocationException * in case there are client or server side system errors, or if * there are application errors in the returned error response. */ public void invoke(String opName, Object[] inParams, ByteBufferWrapper outWrapper) throws ServiceInvocationException { invoke(opName, false, true, inParams, null, null, outWrapper); } private void invoke(String opName, boolean inboundRawMode, boolean outboundRawMode, Object[] inParams, List<Object> outParams, ByteBufferWrapper inWrapper, ByteBufferWrapper outWrapper) throws ServiceInvocationException { RawDataServiceDispatch dispatch = null; try { try { dispatch = new RawDataServiceDispatch(opName, m_serviceLocations, m_serviceDesc, m_wsdlLocation, m_invokerOptions, m_serviceVersion, m_cookies, m_sessionTransportHeaders, m_sessionMessageHeaders, m_g11nOptions, getRequestContext(), null, null); RawDispatchData rawData = new RawDispatchData(inboundRawMode, outboundRawMode, inParams, outParams, inWrapper, outWrapper); // Cache functionality doesn't support call for cache policy // (getCachePolicy) // and "raw" calls boolean cacheSupported = !(SOAConstants.OP_GET_CACHE_POLICY .equals(opName) || inboundRawMode); if (cacheSupported && m_serviceDesc.isCacheDisabledOnLocal() != null && m_serviceDesc.isCacheDisabledOnLocal() .booleanValue()) { if (getInvokerOptions() != null && SOAConstants.TRANSPORT_LOCAL .equalsIgnoreCase(getInvokerOptions() .getTransportName())) { cacheSupported = false; } else if (SOAConstants.TRANSPORT_LOCAL .equalsIgnoreCase(m_serviceDesc .getDefTransportName())) { cacheSupported = false; } } CacheProvider cacheProvider = null; if (cacheSupported) { cacheProvider = m_serviceDesc.getCacheProviderClass(); // if cacheProvider somehow is not available, then we can't // use any caches cacheSupported = cacheProvider != null; if (cacheSupported) { try { cacheProvider .init(m_serviceDesc, (m_serviceLocations==null||m_serviceLocations.isEmpty())? null:m_serviceLocations.get(0)); } catch (ServiceException e) { if (m_serviceDesc.isSkipCacheOnError() != null && !m_serviceDesc.isSkipCacheOnError() .booleanValue()) { throw e; } } } } if (cacheSupported && cacheProvider.isCacheEnabled()) { // cacheContext CacheContext cacheContext = new CacheContext().setOpName( opName).setRequest(inParams[0]); Object result = cacheProvider.lookup(cacheContext); if (result != null) { outParams.add(result); } else { dispatch.invoke(rawData); cacheContext.setResponse(outParams.get(0)); cacheProvider.insert(cacheContext); } } else { // "Old" behavior, which is a direct call to server dispatch.invoke(rawData); return; } } catch (WebServiceException e) { throw e.getCause(); } } catch (RuntimeException e) { throw e; } catch (Error e) { throw e; } catch (ServiceInvocationException e) { throw e; } catch (Throwable e) { throw new WebServiceException(e); } finally { if (dispatch != null) { m_urlPathInfo = dispatch.getUrlPathInfo(); m_requestContext = dispatch.getDispatchRequestContext(); m_responseContext = dispatch.getDispatchResponseContext(); m_numTries = dispatch.getLastTryCount(); } } } /** * This functions help user create a dispatch object which can also be used * to invoke a given service operation in Sync and Aysnc (Pull & Push) mode. * * This specific variant of createDispatch provides the user the ability to * invoke the request by passing in JaxB request object and get a JaxB * response object. * * @param opName * the name of the operation to be invoked * @return <code>ServiceDispatch</code> dispatch object */ public ServiceDispatch createDispatch(String opName) { try { return new TypedDataServiceDispatch(opName, m_serviceLocations, m_serviceDesc, m_wsdlLocation, m_invokerOptions, m_serviceVersion, m_cookies, m_sessionTransportHeaders, m_sessionMessageHeaders, m_g11nOptions, getRequestContext(), m_executor, m_poller); } catch (ServiceException e) { throw new WebServiceException(e); } } /** * This functions help user create a dispatch object which can also be used * to invoke a given service operation in Sync and Aysnc (Pull & Push) mode. * * This specific variant of createDispatch provides the user the ability to * invoke the request by passing in JaxB request object and get a JaxB * response object. * * @param opName * the name of the operation to be invoked * @param isRaw * <code>true</code> if user would like to exchange * request/response using InvokerExchange (allows exchange * of raw packets) * <code>false</code> if user would like to exchange * request/response as JaxB objects. * @return <code>ServiceDispatch</code> dispatch object */ public ServiceDispatch createDispatch(String opName, boolean isRaw) { try { return isRaw ? new RawDataServiceDispatch(opName, m_serviceLocations, m_serviceDesc, m_wsdlLocation, m_invokerOptions, m_serviceVersion, m_cookies, m_sessionTransportHeaders, m_sessionMessageHeaders, m_g11nOptions, getRequestContext(), m_executor, m_poller) : new TypedDataServiceDispatch(opName, m_serviceLocations, m_serviceDesc, m_wsdlLocation, m_invokerOptions, m_serviceVersion, m_cookies, m_sessionTransportHeaders, m_sessionMessageHeaders, m_g11nOptions, getRequestContext(), m_executor, m_poller); } catch (ServiceException e) { throw new WebServiceException(e); } } private static Executor createDefaultExecutor() { ThreadPoolConfig tpConfig = new ThreadPoolConfig(null, ThreadPoolConfig.DFLT_KEEP_ALIVE_TIME_IN_SEC); String poolName = buildThreadPoolName(THREAD_POOL_NAME_PREFIX, THREAD_POOL_NAME_Suffix, !tpConfig.isUseCmdRunner()); return ThreadPoolFactory.getInstance().createExecutor(poolName, tpConfig.getKeepAliveTimeInSec(), tpConfig.getCmdRunnerProps()); } private static String buildThreadPoolName(String prefix, String suffix, boolean appendSystemTime) { StringBuilder sb = new StringBuilder(50); sb.append(prefix).append(THREAD_POOL_NAME_SEPARATOR).append(suffix); if (appendSystemTime) { sb.append(THREAD_POOL_NAME_SEPARATOR); sb.append(System.currentTimeMillis()); } return sb.toString(); } /** * Current executor in context of this service for Async push operations * that require multi-threading support. * * @return <code>Executor</code> Current executor in context of this service */ public Executor getExecutor() { return m_executor; } /** * Allows user to set the current executor in context of this service for * push operations. * * @param executor * Executor to use in context of this service for push * operations. If user passes "null" then push will be executed * on the kernel thread that provided the response. */ public void setExecutor(Executor executor) { m_executor = executor; } /** * Returns the total number of attempted service invocation tries during the * most recent call to invoke(). The total number of attempted invocations * is one plus the number of retries attempted. * * @return the number of attempted invocations, or zero if invoke() has * never been called for this Service object. */ public int getLastTryCount() { return m_numTries; } }